package com.zyx.crawlerdemo.httpclient.httpclient.thread;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.http.Consts;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.*;

/**
 * @author Yaxi.Zhang
 * @since 2021/8/11 17:54
 * desc: 创建带有线程池的Httpclient案例
 */
public class HttpclientPool {
    public static void main(String[] args) throws FileNotFoundException {
        // 添加连接参数
        ConnectionConfig connectionConfig = ConnectionConfig.custom()
                .setMalformedInputAction(CodingErrorAction.IGNORE)
                .setUnmappableInputAction(CodingErrorAction.IGNORE)
                .setCharset(Consts.UTF_8)
                .build();
        // 添加socket参数
        SocketConfig socketConfig = SocketConfig.custom()
                .setTcpNoDelay(true)
                .build();
        // 配置连接池管理器
        PoolingHttpClientConnectionManager pcm = new PoolingHttpClientConnectionManager();
        // 设置最大连接数
        pcm.setMaxTotal(100);
        // 设置每个连接的路由数
        pcm.setDefaultMaxPerRoute(10);
        // 设置连接信息
        pcm.setDefaultConnectionConfig(connectionConfig);
        // 设置socket信息
        pcm.setDefaultSocketConfig(socketConfig);
        // 设置全局请求配置,包括Cookie规范,HTTP认证,超时
        RequestConfig defaultConfig = RequestConfig.custom()
                .setCookieSpec(CookieSpecs.STANDARD_STRICT)
                .setExpectContinueEnabled(true)
                .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
                .setProxyPreferredAuthSchemes(Collections.singletonList(AuthSchemes.BASIC))
                .setConnectionRequestTimeout(30 * 1000)
                .setConnectTimeout(30 * 1000)
                .setSocketTimeout(30 * 1000)
                .build();
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(pcm)
                .setDefaultRequestConfig(defaultConfig)
                .build();
        // 请求的URL
        String[] urlArr = {
                "https://hbr.org/podcasts",
                "https://hbr.org/magazine",
                "https://hbr.org/most-popular",
                "https://hbr.org/big-ideas",
                "https://hbr.org/reading-lists"
        };
        // 创建固定大小的线程池
        final ThreadFactory basicThreadFactory = new BasicThreadFactory.Builder().namingPattern("demo-pool-%d").build();
        final ExecutorService exec = new ThreadPoolExecutor(3, 3, 1000, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10), basicThreadFactory, new ThreadPoolExecutor.AbortPolicy());
        for (String s : urlArr) {
            // HTML需要输出的文件名
            String filename = s.split("org/")[1];
            // 创建HTML文件输出目录
            OutputStream out = new FileOutputStream("file/" + filename);
            HttpGet httpget = new HttpGet(s);
            // 启动线程执行请求
            exec.execute(new DownHtmlFileThread(httpClient, httpget, out));
        }
        // 关闭线程
        exec.shutdown();
    }

    static class DownHtmlFileThread extends Thread {

        private final CloseableHttpClient httpClient;
        private final HttpContext context;
        private final HttpGet httpget;
        private final OutputStream out;

        // 输入的参数
        public DownHtmlFileThread(CloseableHttpClient httpClient, HttpGet httpget, OutputStream out) {
            this.httpClient = httpClient;
            this.context = HttpClientContext.create();
            this.httpget = httpget;
            this.out = out;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "线程请求的URL为:" + httpget.getURI());
            try {
                // 执行请求
                try (CloseableHttpResponse response = httpClient.execute(httpget, context)) {
                    // HTML文件写入文档
                    out.write(EntityUtils.toString(response.getEntity(), "gbk").getBytes());
                    out.close();
                    // 消耗实体
                    EntityUtils.consume(response.getEntity());
                }
                // 关闭响应
            } catch (IOException ex) {
                // 处理I/O错误
                ex.printStackTrace();
            }
        }
    }
}
